﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Collections.ObjectModel;
using WcfContrib.Client.Behaviors;

namespace WcfContrib.Client.Description
{
    /// <summary>
    /// Represents a client channel contract description
    /// </summary>
    public sealed class ClientContractDescription
    {
        Dictionary<string, ClientOperationDescription> operationsStore;

        List<ClientOperationDescription> operations;
        /// <summary>
        /// Gets the contract operations.
        /// </summary>
        /// <value>The contract operations.</value>
        public ReadOnlyCollection<ClientOperationDescription> Operations
        {
            get { return operations.AsReadOnly(); }
        }

        /// <summary>
        /// Gets the contract behaviors.
        /// </summary>
        /// <value>The contract behaviors.</value>
        public DescriptionCollection<IClientContractBehavior> Behaviors { get; private set; }

        /// <summary>
        /// Gets the error handlers.
        /// </summary>
        /// <value>The error handlers.</value>
        public DescriptionCollection<IClientErrorHandler> ErrorHandlers { get; private set; }

        /// <summary>
        /// Gets the type of the service contract.
        /// </summary>
        /// <value>The type of the service contract.</value>
        public Type ContractType { get; private set; }

        /// <summary>
        /// Initializes a new instance of the <see cref="ClientContractDescription"/> class.
        /// </summary>
        /// <param name="contractType">The type of the service contract.</param>
        public ClientContractDescription(Type contractType)
        {
            operations = new List<ClientOperationDescription>();
            operationsStore = new Dictionary<string, ClientOperationDescription>();

            Behaviors = new DescriptionCollection<IClientContractBehavior>();
            ErrorHandlers = new DescriptionCollection<IClientErrorHandler>();

            ContractType = contractType;
        }

        internal void AddOperationDescription(ClientOperationDescription operationDescription)
        {
            operationsStore.Add(operationDescription.OperationName, operationDescription);

            operations.Add(operationDescription);
        }

        internal ClientOperationDescription GetOperationDescription(string operationName)
        {
            ClientOperationDescription desc;
            operationsStore.TryGetValue(operationName, out desc);

            return desc;
        }

        internal ClientContractDescription CloneCore()
        {
            ClientContractDescription desc = new ClientContractDescription(ContractType);

            desc.Behaviors.AddRange(Behaviors);
            desc.ErrorHandlers.AddRange(ErrorHandlers);

            foreach (var operation in operations)
            {
                ClientOperationDescription cloned = operation.CloneCore();

                desc.operationsStore.Add(cloned.OperationName, cloned);

                desc.operations.Add(cloned);
            }

            desc.Behaviors.ItemAdded = b => b.ApplyClientBehavior(desc);

            return desc;
        }

        internal void LoadContract(Type contractType)
        {
            var contractBehaviors = contractType.GetCustomAttributes(true).Where(o => o is IClientContractBehavior).OfType<IClientContractBehavior>();
            Behaviors.AddRange(contractBehaviors, false);
        }

        internal void OnLoadComplete()
        {
            foreach (var behavior in Behaviors)
            {
                behavior.ApplyClientBehavior(this);
            }

            foreach (var oper in Operations)
            {
                oper.OnLoadComplete();
            }
        }
    }
}
